#ifndef _debugstream_h_
#define _debugstream_h_

// debugstream.h
//
//    General-purpose debug stream, like std::cout


#include <ostream>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>

namespace simple{
    //
    // DebugSink
    //
    // DebugSink and derived classes are used to specify the behavior of what
    // happens to output sent to the cdebug stream. A DebugStringBuf<>
    // object is defined in this file which derives from std::basic_stringbuf.
    // This object is used when constructing cdebug (which is a std::ostream
    // object). The stream objects can definitely get confusing. The objects
    // presented here show an easy way to hook into streams for your own
    // purposes. There are more elegant ways but they require more knowledge
    // about streams than most people need to worry about.
    //
    // DebugSink        Abstract base class that defines a simple
    //                      debugging stream.
    // DebugSinkNull    Does nothing
    // DebugSinkConsole Writes debugging information to std::cout
    // DebugSinkFile    Writes debugging information to the 
    //                      specified file.
    //
    // Common overrides (of DebugSink)
    //
    //     header(). Override to change the standard header that is
    //                         displayed, if enabled

    class DebugSink
    {
    public:
        DebugSink ();

        virtual void write (const std::string& str) = 0;
        // Write a string to our debugging sink;

        virtual void write (int c) = 0;
        // Write a character to our debugging sink. Usually called from
        // a std::basic_stringbuf derived object as characters are received.

        virtual void flush () {}
        // flush any stored information if possible for this type of sink.

        virtual std::string header () { return standardHeader();}
        // By default, we emit our standard header when headers are enabled

        void showHeader (bool state) {    _enableHeader = state;}
        // Set the state of whether a header is written

    protected:
        std::string standardHeader ();
        // The standard header consists of the current time

        bool _enableHeader; // true to write header whenever buffer is flushed
    };


    //
    // DebugSinkNull
    //
    // Null sink

    class DebugSinkNull : public DebugSink
    {
    public:
        static DebugSinkNull sOnly;

        virtual void write (const std::string& /*str*/) {}
        virtual void write (int /*c*/) {}

    private:
        DebugSinkNull ();
    };


    //
    // DebugSinkConsole
    //
    // Console sink. Data is written to std::cout
    //
    // Common overrides
    //
    //  display(). Override to change the output source. Default is std::cout.
    //             See DebugSinkFile for an example of this.

    class DebugSinkConsole : public DebugSink
    {
    public:
        static DebugSinkConsole sOnly;

        virtual void write (const std::string& str);
        virtual void write (int c);
        virtual void flush ();

    protected:
        virtual void display (const std::string& str);
        // Output the string. Derived classes can override this

        DebugSinkConsole ();
        virtual ~DebugSinkConsole ();

        std::string _buffer;
    };


    //
    // DebugSinkFile
    //
    // File sink

    class DebugSinkFile : public DebugSinkConsole
    {
    public:
        static DebugSinkFile sOnly;

        void setFile (const std::string& file);
        // Set/change our file name. The stream is flushed before the
        // file name is changed.

    private:
        virtual void display (const std::string& str);

        DebugSinkFile ();
        virtual ~DebugSinkFile ();

        std::string _file;
    };



    //
    // DebugStringBuf
    //
    // We need to derive a class from std::basic_stringbuf<> to intercept
    // characters for our debugging sinks. DebugStringBuf bridges the
    // gap by intercepting stream characters by overriding overflow() and
    // passing the characters to the current sink

    template<class T, class Tr = std::char_traits<T>, class A = std::allocator<T> >
    class DebugStringBuf : public std::basic_stringbuf<T, Tr, A>
    {
    public:
        DebugStringBuf (DebugSink& s = DebugSinkNull::sOnly) : _sink (&s) {}
        ~DebugStringBuf () { _sink->flush();}

        typedef typename std::basic_stringbuf<T, Tr, A>::int_type int_type;

        int_type overflow (int_type c) {
            if (c != traits_type::eof())
                _sink->write (c);
            return traits_type::not_eof(c);
        }

        void sink (DebugSink& s) { _sink = &s;}
        // Change our sink

    private:
        DebugStringBuf (const DebugStringBuf&);    // Do not allow copies
        DebugStringBuf& operator= (const DebugStringBuf&);    // Do not allow copies

        DebugSink* _sink;
    };


    // Our global instance we will use to write debugging information
    // You can use cdebug just like std::cout. The default sink is 
    // DebugSinkNull so you'll need to set it to something, before
    // using cdebug, ex:
    //     debugstream.sink (DebugSinkConsole::sOnly);

    extern DebugStringBuf<char> debugstream;
    extern std::ostream cdebug;

}
#endif // _debugstream_h_
